package org.example.util;

import org.example.bean.BoundingBox;
import org.example.bean.GeoPoint;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

// 四至 工具类
public class FourBoundariesGraphUtil {

    public static double[] findMinMax(List<Double> list) {
        double min = Double.MAX_VALUE, max = Double.MIN_VALUE;
        for (int i = 0; i < list.size(); i++) {
            max = Math.max(max, list.get(i));
            min = Math.min(min, list.get(i));
        }
        return new double[] { min, max };
    }

    public static String getTileNumber(final double lat, final double lon, final int zoom) {
        int xtile = (int) Math.floor((lon + 180) / 360 * (1 << zoom));
        int ytile = (int) Math.floor((1 - Math.log(Math.tan(Math.toRadians(lat)) + 1 / Math.cos(Math.toRadians(lat))) / Math.PI) / 2 * (1 << zoom));
        if (xtile < 0)
            xtile = 0;
        if (xtile >= (1 << zoom))
            xtile = ((1 << zoom) - 1);
        if (ytile < 0)
            ytile = 0;
        if (ytile >= (1 << zoom))
            ytile = ((1 << zoom) - 1);
        return (zoom + "/" + xtile + "/" + ytile);
    }

    public static BoundingBox tile2boundingBox(final int x, final int y, final int zoom) {
        return new BoundingBox(tile2lat(y, zoom), tile2lat(y + 1, zoom), tile2lon(x + 1, zoom), tile2lon(x, zoom));
    }

    public static double tile2lon(int x, int z) {
        return x / Math.pow(2.0, z) * 360.0 - 180;
    }

    public static double tile2lat(int y, int z) {
        double n = Math.PI - (2.0 * Math.PI * y) / Math.pow(2.0, z);
        return Math.toDegrees(Math.atan(Math.sinh(n)));
    }

    public static int[] getRowCol(double lg, double la, int zoom) {
        return new int[] { (int) (Math.floor((90 - la) / (180 / Math.pow(2, zoom - 1)))) + 1, (int) Math.floor((lg + 180) / (360 / Math.pow(2, zoom))) + 1 };
    }

    private static final double EARTH_RADIUS = 6378137.0;
    private static final double MAX_LATITUDE = 85.05112877980659;

    /**
     * 将纬度转换为墨卡托投影坐标
     *
     * @param latitude 纬度
     * @return 墨卡托投影坐标
     */
    private static double latToMercator(double latitude) {
        double x = EARTH_RADIUS * Math.toRadians(latitude);
        x /= Math.cos(Math.toRadians(latitude));
        return x;
    }

    /**
     * 计算瓦片行列号
     *
     * @param latitude  纬度
     * @param longitude 经度
     * @param zoom      缩放级别
     * @return 瓦片行列号数组，格式为 [col, row]
     */
    public static int[] calculateTileXY(double latitude, double longitude, int zoom) {
        double n = Math.pow(2, zoom);
        double mercX = (longitude + 180) / 360;
        double mercY = (1 - Math.log(Math.tan(Math.toRadians(latitude)) + 1 / Math.cos(Math.toRadians(latitude))) / Math.PI) / 2;

        int tileX = (int) Math.floor(mercX * n);
        int tileY = (int) Math.floor(mercY * n);

        return new int[] { tileX, tileY };
    }

    public static void test() {
        int zoom = 10;
        double lat = 30.968056d;
        double lon = 121.909167d;
        System.out.println("https://tile.openstreetmap.org/" + getTileNumber(lat, lon, zoom) + ".png");
        System.out.println(Arrays.toString(getRowCol(lon, lat, zoom)));
        System.out.println(Arrays.toString(calculateTileXY(lat, lon, zoom)));
    }

    public static void test2() {
        int zoom = 10;
        double lat = 30.968056d;
        double lon = 121.909167d;
        double[] rect_pts = new double[] { 121.48d, 31.23d, 121.76d, 31.06d };
        int[] tiandituInfo = get_tianditu_info(rect_pts, zoom);
        System.out.println(Arrays.toString(get_tianditu_info(rect_pts, zoom)));
        for (int i = tiandituInfo[0]; i <= tiandituInfo[1]; i++) {
            for (int j = tiandituInfo[2]; j <= tiandituInfo[3]; j++) {
                System.out.println(" http://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile" + "&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX=" + zoom + "&TILEROW=" + j + "&TILECOL=" + i + "&tk=68d166cfe304fa077ff035bed00edc37");
            }
        }
    }

    private static int[] get_tianditu_info(double[] rect_pts, int zoom) {
//    根据经纬度计算行列号
//    :param rect_pts: 矩形框坐标 例[a,b,c,d]左上点，右下点
//    :param zoom:地图层级
        HashMap<Integer, Double> map = new HashMap<>();
        map.put(1, 0.703125);
        map.put(2, 0.3515625);
        map.put(3, 0.17578125);
        map.put(4, 0.087890625);
        map.put(5, 0.0439453125);
        map.put(6, 0.02197265625);
        map.put(7, 0.010986328125);
        map.put(8, 0.0054931640625);
        map.put(9, 0.00274658203125);
        map.put(10, 0.001373291015625);
        map.put(11, 0.0006866455078125);
        map.put(12, 0.00034332275390625);
        map.put(13, 0.000171661376953125);
        map.put(14, 8.58306884765629E-05);
        map.put(15, 4.29153442382814E-05);
        map.put(16, 2.1457672119140625E-05);
        map.put(17, 1.07288360595703E-05);
        map.put(18, 5.36441802978515E-06);

//        resolution = {18:5.36441802978515E-06,
//                17:1.07288360595703E-05,
//                16:2.1457672119140625E-05,
//                15:4.29153442382814E-05,
//                14:8.58306884765629E-05,
//                13:0.000171661376953125,
//                12:0.00034332275390625,
//                11:0.0006866455078125,
//                10:0.001373291015625,
//                9:0.00274658203125,
//                8:0.0054931640625,
//                7:0.010986328125,
//                6:0.02197265625,
//                5:0.0439453125,
//                4:0.087890625,
//                3:0.17578125,
//                2:0.3515625,
//                1:0.703125}
//    经纬度反算切片行列号
        double start_x = Math.floor((rect_pts[0] + 180.0) / (map.get(zoom) * 256));
        double start_y = Math.floor((90.0 - rect_pts[1]) / (map.get(zoom) * 256));
        double end_x = Math.ceil((rect_pts[2] + 180.0) / (map.get(zoom) * 256));
        double end_y = Math.ceil((90.0 - rect_pts[3]) / (map.get(zoom) * 256));
        int[] result = new int[] { (int) start_x, (int) end_x, (int) start_y, (int) end_y };
        return result;
    }

    public static void test3() {
        int zoom = 10;
        double lat = 30.968056d;
        double lon = 121.909167d;
//        System.out.println("https://tile.openstreetmap.org/" + getTileNumber(lat, lon, zoom) + ".png");
//        System.out.println(getTileNumber(lat,lon,zoom));
//        System.out.println(Arrays.toString(getRowCol(lon,lat,zoom)));
        double[] rect_pts = new double[] { 121.909167d, 30.968056d, 122.109167d, 30.068056d };
        System.out.println(Arrays.toString(getRowCol(rect_pts[0], rect_pts[1], zoom)));
        System.out.println(Arrays.toString(getRowCol(rect_pts[2], rect_pts[3], zoom)));
        System.out.println(Arrays.toString(get_tianditu_info(rect_pts, zoom)));

    }

    public static void test4() {
        double latitude = 33.753801195570944;
        double longitude = 118.73238532932953;
        int zoom = 10; // 缩放级别

        int[] tileXY = calculateTileXY(latitude, longitude, zoom);
        System.out.println("Tile X: " + tileXY[0] + ", Tile Y: " + tileXY[1]);
//        System.out.println(calculateTileXY(latitude, longitude, zoom));
        System.out.println(Arrays.toString(getRowCol(longitude, latitude, zoom)));
    }

    public static void test5() {
        double[] rect_pts = new double[] { 121.909167d, 30.968056d, 122.109167d, 30.068056d };
        int zoom = 10;
        int[] leftUp = calculateTileXY(rect_pts[1], rect_pts[0], zoom);
        int[] rightDown = calculateTileXY(rect_pts[3], rect_pts[2], zoom);
        System.out.println(Arrays.toString(leftUp));
        System.out.println(Arrays.toString(rightDown));
        for (int i = leftUp[0]; i <= rightDown[0]; i++) {
            for (int j = leftUp[1]; j <= rightDown[1]; j++) {
                System.out.println(" http://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile" + "&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX=" + zoom + "&TILEROW=" + j + "&TILECOL=" + i + "&tk=68d166cfe304fa077ff035bed00edc37");
            }
        }
    }

    public static void test6() {
        BoundingBox boundingBox = tile2boundingBox(107, 52, 7);

        System.out.println((30.968056d - boundingBox.getSouth()) / (boundingBox.getNorth() - boundingBox.getSouth()) * 256);
        System.out.println((121.909167d - boundingBox.getWest()) / (boundingBox.getEast() - boundingBox.getWest()) * 256);
    }

    public static void fourBoundariesGraph() {
        List<GeoPoint> list = Arrays.asList(new GeoPoint(121.19090842455603, 30.796731658279672), new GeoPoint(121.19090842455603, 30.796731658279672), new GeoPoint(121.19090842455603, 30.796731658279672), new GeoPoint(121.19090842455603, 30.796731658279672), new GeoPoint(121.19090842455603, 30.796731658279672));
        List<Double> xlist = list.stream().map(GeoPoint::getX).collect(Collectors.toList());
        List<Double> ylist = list.stream().map(GeoPoint::getY).collect(Collectors.toList());
        double[] xMinMax = findMinMax(xlist);
        double[] yMinMax = findMinMax(ylist);
        // BBox 取左上 和 右下
        StringBuilder sb = new StringBuilder();

        String bbox = sb.append(xMinMax[0]).append(",").append(yMinMax[1]).append(",").append(xMinMax[1]).append(",").append(yMinMax[0]).toString();
        // 输出图片的宽和高，像素级，可以任意设置
        String width = "768";
        String height = "332";
        String srs = "EPSG:4326";
        String format = "image/jpeg";

        String url = "http://192.168.0.38:8092/geoserver/test/wms?SERVICE=WMS" + "&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image/jpeg&TRANSPARENT=true" + "&STYLES&LAYERS=test:廊下2022航片_43261_PZ&exceptions=application/vnd.ogc.se_inimage" + "&SRS=EPSG:4326&WIDTH=768&HEIGHT=332" + "&BBOX=121.19090842455603,30.796731658279672,121.19142336398386,30.796954236924872";

        String tiandituUrl = "http://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS" + "&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&" + "FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}" + "&tk=ee5b1beee9ba49d8b620d88522f78fa1";
//        HttpOkUtil.downloadAsynByte();
        HttpOkUtil.downloadImg(tiandituUrl, "");
    }
    public static void main(String[] args) {
//        fourBoundariesGraph();
//        test2();
//        test3();
//        test4();
//        test();
        test5();
//        test6();
    }
}

